package cn.wlinker.video.monitor.stream;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 转换器工厂
 *
 * @author gxsjx
 * @version 1.0
 * @date 2025/5/3
 * Copyright © wlinker.cn
 */
@Slf4j
public class ConverterFactories extends Thread implements Converter {

    /**
     * 运行状态
     */
    /**
     * 读流器
     */
    private FFmpegFrameGrabber grabber;
    /**
     * 转码器
     */
    private FFmpegFrameRecorder recorder;
    /**
     * 转FLV格式的头信息<br/>
     * 如果有第二个客户端播放首先要返回头信息
     */
    private byte[] headers;
    /**
     * 保存转换好的流
     */
    private ByteArrayOutputStream stream;
    /**
     * 流地址，h264,aac
     */
    private final String url;
    /**
     * 流输出
     */
    private Map<String, OutputStreamEntity> outEntities;

    /**
     * 当前转换器状态
     */
    private ConverterState state = ConverterState.INITIAL;
    /**
     * key用于表示这个转换器
     */
    private final String key;
    /**
     * 上次更新时间<br/>
     * 客户端读取是刷新<br/>
     * 如果没有客户端读取，会在一分钟后销毁这个转换器
     */
    private long updateTime;

    private final InputStream inputStream;

    public ConverterFactories(String url, String key) {
        this.url = url;
        this.key = key;
        this.updateTime = System.currentTimeMillis();
        this.inputStream = null;
    }

    public ConverterFactories(String originId, InputStream inputStream, String key) {
        this.url = originId;
        this.key = key;
        this.updateTime = System.currentTimeMillis();
        this.inputStream = inputStream;
    }

    @Override
    public void run() {
        synchronized (url.intern()) {
            try {
                //使用ffmpeg抓取流，创建读流器
                if (inputStream == null) {
                    grabber = new FFmpegFrameGrabber(url);
                } else {
                    grabber = new FFmpegFrameGrabber(inputStream);
                }
                //如果为rtsp流，增加配置
                if ("rtsp".equals(url.substring(0, 4))) {
                    //设置打开协议tcp / udp
                    grabber.setOption("rtsp_transport", "tcp");
                    //设置未响应超时时间 0.5秒
                    grabber.setOption("stimeout", "500000");
                    //设置缓存大小，提高画质、减少卡顿花屏
                    grabber.setOption("buffer_size", "1024000");
                    //设置视频比例
                    //grabber.setAspectRatio(1.7777);
                } else {
                    // 海康默认PS流封装
                    grabber.setFormat("mpeg");
                    grabber.setOption("stimeout", "500000");
                    //设置缓存大小，提高画质、减少卡顿花屏
                    grabber.setOption("buffer_size", "1024000");
                }
                grabber.start();
                stream = new ByteArrayOutputStream();
                outEntities = new ConcurrentHashMap<>(16);
                //设置转换状态为打开
                state = ConverterState.OPEN;
                //创建转码器
                recorder = new FFmpegFrameRecorder(
                        stream, grabber.getImageWidth(),
                        grabber.getImageHeight(),
                        grabber.getAudioChannels());

                //配置转码器
                configRecorder(recorder, grabber);
                recorder.start(grabber.getFormatContext());
                //进入写入运行状态
                state = ConverterState.RUN;
                if (headers == null) {
                    headers = stream.toByteArray();
                    stream.reset();
                    for (OutputStreamEntity o : outEntities.values()) {
                        o.getOutput().write(headers);
                    }
                }
                int errorNum = 0;
                //线程运行时
                while (isRunning()) {
                    //FFmpeg读流压缩
                    AVPacket k = grabber.grabPacket();
                    if (k != null) {
                        try {
                            //转换器转换
                            recorder.recordPacket(k);
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }
                        byte[] b = stream.toByteArray();
                        stream.reset();
                        for (OutputStreamEntity o : outEntities.values()) {
                            if (o.getOutput().size() < (1024 * 1024)) {
                                o.getOutput().write(b);
                            }
                        }
                        errorNum = 0;
                    } else {
                        errorNum++;
                        if (errorNum > 500) {
                            break;
                        }
                    }
                    if (inputStream != null) {
                        Thread.sleep(8);
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                state = ConverterState.ERROR;
            } finally {
                closeConverter();
                log.info("exit");
                state = ConverterState.CLOSE;
                ConverterRegistration.removeConverterByUrl(url);
            }
        }
    }

    public static void configRecorder(FFmpegFrameRecorder recorder, FFmpegFrameGrabber grabber) {
        recorder.setFrameRate(grabber.getFrameRate());
        recorder.setSampleRate(grabber.getSampleRate());
        if (grabber.getAudioChannels() > 0) {
            recorder.setAudioChannels(grabber.getAudioChannels());
            recorder.setAudioBitrate(grabber.getAudioBitrate());
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            //设置视频比例
            //recorder.setAspectRatio(grabber.getAspectRatio());
        }
        recorder.setFormat("flv");
        recorder.setVideoBitrate(grabber.getVideoBitrate());
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
    }

    /**
     * 退出转换
     */
    public void closeConverter() {
        try {
            //停止转码器
            if (null != recorder) {
                recorder.stop();
            }
            //停止、关闭读流器
            grabber.stop();
            grabber.close();
            //关闭转码器
            if (null != recorder) {
                recorder.close();
            }
            //关闭流
            if (null != stream) {
                stream.close();
            }
            if (null != outEntities) {
                for (OutputStreamEntity o : outEntities.values()) {
                    o.getOutput().close();
                }
            }
        } catch (Exception e) {
            log.error("退出转换异常", e);
        }
    }

    @Override
    public String getKey() {
        return this.key;
    }

    @Override
    public String getUrl() {
        return this.url;
    }

    @Override
    public ConverterState getConverterState() {
        return this.state;
    }

    @Override
    public void addOutputStreamEntity(String key, OutputStreamEntity entity) {
        while (true) {
            try {
                switch (this.state) {
                    case INITIAL:
                        Thread.sleep(100);
                        continue;
                    case OPEN:
                        outEntities.put(key, entity);
                        return;
                    case RUN:
                        entity.getOutput().write(this.headers);
                        outEntities.put(key, entity);
                        return;
                    default:
                        return;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return;
            }
        }
    }

    @Override
    public void setUpdateTime(long updateTime) {
        this.updateTime = updateTime;
    }

    @Override
    public long getUpdateTime() {
        return this.updateTime;
    }

    @Override
    public void exit() {
        setState(ConverterState.CLOSE);
        //设置线程状态为非运行状态，最后会进入finally块关闭读流器、转码器、流
        try {
            this.join();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public OutputStreamEntity getOutputStream(String key) {
        if (outEntities.containsKey(key)) {
            return outEntities.get(key);
        }
        return null;
    }

    @Override
    public Map<String, OutputStreamEntity> allOutEntity() {
        return this.outEntities;
    }

    @Override
    public void removeOutputStreamEntity(String key) {
        this.outEntities.remove(key);
    }

    @Override
    public boolean isRunning() {
        return !state.equals(ConverterState.CLOSE) && !state.equals(ConverterState.ERROR);
    }

    @Override
    public void setState(ConverterState state) {
        this.state = state;
    }
}